package com.daona.security.service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Service
public class JwtService {

  /**
   * 加密盐值
   */
  @Value("${application.security.jwt.secret-key}")
  private String secretKey;

  /**
   * Token失效时间
   */
  @Value("${application.security.jwt.expiration}")
  private long jwtExpiration;

  /**
   * Token刷新时间
   */
  @Value("${application.security.jwt.refresh-token.expiration}")
  private long refreshExpiration;

  /**
   * 从Token中获取Username
   * @param token
   * @return
   */
  public String extractUsername(String token) {
    return extractClaim(token, Claims::getSubject);
  }

  /**
   * 从Token中回去数据,根据传入不同的Function返回不同的数据
   * @param token
   * @param claimsResolver
   * @return
   * @param <T>
   */
  public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
    final Claims claims = extractAllClaims(token);
    return claimsResolver.apply(claims);
  }

  /**
   * 生成Token无额外信息
   */
  public String generateToken(UserDetails userDetails) {
    return generateToken(new HashMap<>(), userDetails);
  }

  /**
   * 生成Token,有额外信息
   * @param extraClaims 额外的数据
   * @param userDetails 用户信息
   * @return String
   */
  public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) {
    return buildToken(extraClaims, userDetails, jwtExpiration);
  }

  /**
   * 生成刷新用的Token
   * @param userDetails 用户信息
   * @return String
   */
  public String generateRefreshToken(UserDetails userDetails) {
    return buildToken(new HashMap<>(), userDetails, refreshExpiration);
  }

  /**
   * 构建Token方法
   * @param extraClaims 额外信息
   * @param userDetails //用户信息
   * @param expiration //失效时间
   * @return String
   */
  private String buildToken(Map<String, Object> extraClaims, UserDetails userDetails, long expiration) {

    return Jwts
            .builder()
            .claims(extraClaims) //body
            .subject(userDetails.getUsername()) //主题数据
            .issuedAt(new Date(System.currentTimeMillis())) //设置发布时间
            .expiration(new Date(System.currentTimeMillis() + expiration)) //设置过期时间
            .signWith(getSignInKey(), Jwts.SIG.HS256) //设置摘要算法
            .compact();
  }

  /**
   * 验证Token是否有效
   * @param token Token
   * @param userDetails 用户信息
   * @return boolean
   */
  public boolean isTokenValid(String token, UserDetails userDetails) {
    final String username = extractUsername(token);
    return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
  }

  /**
   * 判断Token是否过期
   */
  private boolean isTokenExpired(String token) {
    return extractExpiration(token).before(new Date());
  }

  /**
   * 从Token中获取失效时间
   */
  private Date extractExpiration(String token) {
    return extractClaim(token, Claims::getExpiration);
  }

  /**
   * 从Token中获取所有数据
   */
  private Claims extractAllClaims(String token) {
    Jws<Claims> claimsJws = Jwts.parser()
            .verifyWith(getSignInKey())  //设置签名的密钥
            .build()
            .parseSignedClaims(token); //设置要解析的jwt
    return claimsJws.getPayload();
  }

  /**
   * 获取签名Key
   * Token 加密解密使用
   */
  private SecretKey getSignInKey() {
    byte[] keyBytes = Decoders.BASE64.decode(secretKey);
    return Keys.hmacShaKeyFor(keyBytes);
  }
}
